Using deep features to build an image classifier

Fire up GraphLab Create


In [1]:
import graphlab

Load a common image analysis dataset

We will use a popular benchmark dataset in computer vision called CIFAR-10.

(We've reduced the data to just 4 categories = {'cat','bird','automobile','dog'}.)

This dataset is already split into a training set and test set.


In [2]:
image_train = graphlab.SFrame('image_train_data/')
image_test = graphlab.SFrame('image_test_data/')


[INFO] This non-commercial license of GraphLab Create is assigned to tuancrazy216@gmail.comand will expire on September 21, 2016. For commercial licensing options, visit https://dato.com/buy/.

[INFO] Start server at: ipc:///tmp/graphlab_server-5804 - Server binary: C:\Anaconda\envs\dato-env\lib\site-packages\graphlab\unity_server.exe - Server log: C:\Users\tvu\AppData\Local\Temp\graphlab_server_1444525110.log.0
[INFO] GraphLab Server Version: 1.6.1

Exercise

1) Computing summary statistics of the data

Sketch summaries are techniques for computing summary statistics of data very quickly. In GraphLab Create, SFrames and SArrays include a method:

.sketch_summary()

which computes such summary statistics. Using the training data, compute the sketch summary of the ‘label’ column and interpret the results. What’s the least common category in the training data?


In [3]:
image_train['label'].sketch_summary()


Out[3]:
+------------------+-------+----------+
|       item       | value | is exact |
+------------------+-------+----------+
|      Length      |  2005 |   Yes    |
| # Missing Values |   0   |   Yes    |
| # unique values  |   4   |    No    |
+------------------+-------+----------+

Most frequent items:
+-------+------------+-----+-----+------+
| value | automobile | cat | dog | bird |
+-------+------------+-----+-----+------+
| count |    509     | 509 | 509 | 478  |
+-------+------------+-----+-----+------+

2) Creating category-specific image retrieval models

In most retrieval tasks, the data we have is unlabeled, thus we call these unsupervised learning problems. However, we have labels in this image dataset, and will use these to create one model for each of the 4 image categories, {‘dog’,’cat’,’automobile’,bird’}. To start, follow these steps:

  • Split the SFrame with the training data into 4 different SFrames. Each of these will contain data for 1 of the 4 categories above. Hint: if you use a logical filter to select the rows where the ‘label’ column equals ‘dog’, you can create an SFrame with only the data for images labeled ‘dog’.

  • Similarly to the image retrieval notebook you downloaded, you are going to create a nearest neighbor model using the 'deep_features' as the features, but this time create one such model for each category, using the training_data. You can call the model with the ‘dog’ data the dog_model, the one with the ‘cat’ data the cat_model, as so on. You now have a nearest neighbors model that can find the nearest ‘dog’ to any image you give it, the dog_model; one that can find the nearest ‘cat’, the cat_model; and so on.

Using these models, answer the following questions. The cat image below is the first in the test data

You can access this image, similarly to what we did in the iPython notebooks above, with this command:

image_test[0:1]
  • What is the nearest ‘cat’ labeled image in the training data to the cat image above (the first image in the test data)?

a) Split training data into 4 different categories


In [4]:
automobile = image_train.filter_by(['automobile'],'label')
cat = image_train.filter_by(['cat'],'label')
dog = image_train.filter_by(['dog'],'label')
bird = image_train.filter_by(['bird'],'label')

b) Create nearest neighbor model for each category


In [5]:
automobile_model  = graphlab.nearest_neighbors.create(automobile, features=['deep_features'],
                                                     label='id')


PROGRESS: Starting brute force nearest neighbors model training.

In [6]:
cat_model  = graphlab.nearest_neighbors.create(cat, features=['deep_features'],
                                                     label='id')


PROGRESS: Starting brute force nearest neighbors model training.

In [7]:
dog_model  = graphlab.nearest_neighbors.create(dog, features=['deep_features'],
                                                     label='id')


PROGRESS: Starting brute force nearest neighbors model training.

In [8]:
bird_model  = graphlab.nearest_neighbors.create(bird, features=['deep_features'],
                                                     label='id')


PROGRESS: Starting brute force nearest neighbors model training.

In [15]:
graphlab.canvas.set_target('ipynb')
image_test[0:1]


Out[15]:
id image label deep_features image_array
0 Height: 32 Width: 32 cat [1.13469004631, 0.0, 0.0,
0.0, 0.0366497635841, ...
[158.0, 112.0, 49.0,
159.0, 111.0, 47.0, ...
[1 rows x 5 columns]


In [16]:
image_test[0:1]['image'].show()



In [91]:
cat_model.query(image_test[0:1])


PROGRESS: Starting pairwise querying.
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 0            | 1       | 0.196464    | 13ms         |
PROGRESS: | Done         |         | 100         | 85ms         |
PROGRESS: +--------------+---------+-------------+--------------+
Out[91]:
query_label reference_label distance rank
0 16289 34.623719208 1
0 45646 36.0068799284 2
0 32139 36.5200813436 3
0 25713 36.7548502521 4
0 331 36.8731228168 5
[5 rows x 4 columns]


In [92]:
def get_images_from_ids(query_result):
    return image_train.filter_by(query_result['reference_label'],'id')

What is the nearest ‘cat’ labeled image in the training data to the cat image above (the first image in the test data)?


In [96]:
cat_image = image_train[image_train['id']==16289]
cat_image['image'].show()



In [93]:
get_images_from_ids(cat_model.query(image_test[0:1]))['image'].show()


PROGRESS: Starting pairwise querying.
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 0            | 1       | 0.196464    | 10.001ms     |
PROGRESS: | Done         |         | 100         | 89.009ms     |
PROGRESS: +--------------+---------+-------------+--------------+

What is the nearest ‘dog’ labeled image in the training data to the cat image above (the first image in the test data)?


In [97]:
dog_model.query(image_test[0:1])


PROGRESS: Starting pairwise querying.
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 0            | 1       | 0.196464    | 9.001ms      |
PROGRESS: | Done         |         | 100         | 69.007ms     |
PROGRESS: +--------------+---------+-------------+--------------+
Out[97]:
query_label reference_label distance rank
0 16976 37.4642628784 1
0 13387 37.5666832169 2
0 35867 37.6047267079 3
0 44603 37.7065585153 4
0 6094 38.5113254907 5
[5 rows x 4 columns]

In [98]:
dog_image = image_train[image_train['id']==16976]
dog_image['image'].show()



In [94]:
get_images_from_ids(dog_model.query(image_test[0:1]))['image'].show()


PROGRESS: Starting pairwise querying.
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 0            | 1       | 0.196464    | 15.002ms     |
PROGRESS: | Done         |         | 100         | 91.009ms     |
PROGRESS: +--------------+---------+-------------+--------------+

3) A simple example of nearest-neighbors classification

When we queried a nearest neighbors model, the ‘distance’ column in the table above shows the computed distance between the input and each of the retrieved neighbors. In this question, you will use these distances to perform a classification task, using the idea of a nearest-neighbors classifier.

  • For the first image in the test data (image_test[0:1]), which we used above, compute the mean distance between this image at its 5 nearest neighbors that were labeled ‘cat’ in the training data (similarly to what you did in the previous question). Save this result.

  • Similarly, for the first image in the test data (image_test[0:1]), which we used above, compute the mean distance between this image at its 5 nearest neighbors that were labeled ‘dog’ in the training data (similarly to what you did in the previous question). Save this result.

  • On average, is the first image in the test data closer to its 5 nearest neighbors in the ‘cat’ data or in the ‘dog’ data? (In a later course, we will see that this is an example of what is called a k-nearest neighbors classifier, where we use the label of neighboring points to predict the label of a test point.)


In [21]:
cat_model.query(image_test[0:1])['distance'].mean()


PROGRESS: Starting pairwise querying.
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 0            | 1       | 0.196464    | 9ms          |
PROGRESS: | Done         |         | 100         | 81ms         |
PROGRESS: +--------------+---------+-------------+--------------+
Out[21]:
36.15573070978294

In [22]:
dog_model.query(image_test[0:1])['distance'].mean()


PROGRESS: Starting pairwise querying.
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 0            | 1       | 0.196464    | 9ms          |
PROGRESS: | Done         |         | 100         | 72ms         |
PROGRESS: +--------------+---------+-------------+--------------+
Out[22]:
37.77071136184156

4) [Challenging Question] Computing nearest neighbors accuracy using SFrame operations

A nearest neighbor classifier predicts the label of a point as the most common label of its nearest neighbors. In this question, we will measure the accuracy of a 1-nearest-neighbor classifier, i.e., predict the output as the label of the nearest neighbor in the training data. Although there are simpler ways of computing this result, we will go step-by-step here to introduce you to more concepts in nearest neighbors and SFrames, which will be useful later in this Specialization.

  • Training models: For this question, you will need the nearest neighbors models you learned above on the training data, i.e., the dog_model, cat_model, automobile_model and bird_model.

  • Spliting test data by label: Above, you split the train data SFrame into one SFrame for images labeled ‘dog’, another for those labeled ‘cat’, etc. Now, do the same for the test data. You can call the resulting SFrames

image_test_cat, image_test_dog, image_test_bird, image_test_automobile

In [23]:
image_test_automobile = image_test.filter_by(['automobile'],'label')
image_test_cat = image_test.filter_by(['cat'],'label')
image_test_dog = image_test.filter_by(['dog'],'label')
image_test_bird = image_test.filter_by(['bird'],'label')
  • Finding nearest neighbors in the training set for each part of the test set: Thus far, we have queried, e.g.,
dog_model.query()

our nearest neighbors models with a single image as the input, but you can actually query with a whole set of data, and it will find the nearest neighbors for each data point. Note that the input index will be stored in the ‘query_label’ column of the output SFrame.

Using this knowledge find the closest neighbor in to the dog test data using each of the trained models, e.g.,

dog_cat_neighbors = cat_model.query(image_test_dog, k=1)

finds 1 neighbor (that’s what k=1 does) to the dog test images (image_test_dog) in the cat portion of the training data (used to train the cat_model).

Now, do this for every combination of the labels in the training and test data.

  • Create an SFrame with the distances from ‘dog’ test examples to the respective nearest neighbors in each class in the training data: The ‘distance’ column in dog_cat_neighbors above contains the distance between each ‘dog’ image in the test set and its nearest ‘cat’ image in the training set. The question we want to answer is how many of the test set ‘dog’ images are closer to a ‘dog’ in the training set than to a ‘cat’, ‘automobile’ or ‘bird’. So, next we will create an SFrame containing just these distances per data point. The goal is to create an SFrame called dog_distances with 4 columns:

i. dog_distances[‘dog-dog’] ---- storing dog_dog_neighbors[‘distance’]

ii. dog_distances[‘dog-cat’] ---- storing dog_cat_neighbors[‘distance’]

iii. dog_distances[‘dog-automobile’] ---- storing dog_automobile_neighbors[‘distance’]

iv. dog_distances[‘dog-bird’] ---- storing dog_bird_neighbors[‘distance’]


In [24]:
dog_cat_neighbors = cat_model.query(image_test_dog, k=1)


PROGRESS: Starting blockwise querying.
PROGRESS: max rows per data block: 7668
PROGRESS: number of reference data blocks: 4
PROGRESS: number of query data blocks: 1
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 1000         | 128000  | 25.1473     | 493ms        |
PROGRESS: | Done         | 509000  | 100         | 539ms        |
PROGRESS: +--------------+---------+-------------+--------------+

In [27]:
dog_dog_neighbors = dog_model.query(image_test_dog, k=1)


PROGRESS: Starting blockwise querying.
PROGRESS: max rows per data block: 7668
PROGRESS: number of reference data blocks: 4
PROGRESS: number of query data blocks: 1
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 1000         | 127000  | 24.9509     | 481ms        |
PROGRESS: | Done         | 509000  | 100         | 518ms        |
PROGRESS: +--------------+---------+-------------+--------------+

In [28]:
dog_automobile_neighbors = automobile_model.query(image_test_dog, k=1)


PROGRESS: Starting blockwise querying.
PROGRESS: max rows per data block: 7668
PROGRESS: number of reference data blocks: 4
PROGRESS: number of query data blocks: 1
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 1000         | 127000  | 24.9509     | 470ms        |
PROGRESS: | Done         | 509000  | 100         | 538ms        |
PROGRESS: +--------------+---------+-------------+--------------+

In [29]:
dog_bird_neighbors = bird_model.query(image_test_dog, k=1)


PROGRESS: Starting blockwise querying.
PROGRESS: max rows per data block: 7668
PROGRESS: number of reference data blocks: 4
PROGRESS: number of query data blocks: 1
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 1000         | 120000  | 25.1046     | 454ms        |
PROGRESS: | Done         | 478000  | 100         | 504ms        |
PROGRESS: +--------------+---------+-------------+--------------+

Hint: You can create a new SFrame from the columns of other SFrames by creating a dictionary with the new columns, as shown in this example:

new_sframe = graphlab.SFrame({‘foo’: other_sframe[‘foo’],‘bar’: some_other_sframe[‘bar’]})

In [33]:
dog_distances = graphlab.SFrame({'dog_automobile': dog_automobile_neighbors['distance'],
                              'dog_bird': dog_bird_neighbors['distance'],
                              'dog_cat': dog_cat_neighbors['distance'],
                              'dog_dog': dog_dog_neighbors['distance']
                             })

In [34]:
dog_distances.head()


Out[34]:
dog_automobile dog_bird dog_cat dog_dog
41.9579761457 41.7538647304 36.4196077068 33.4773590373
46.0021331807 41.3382958925 38.8353268874 32.8458495684
42.9462290692 38.6157590853 36.9763410854 35.0397073189
41.6866060048 37.0892269954 34.5750072914 33.9010327697
39.2269664935 38.272288694 34.778824791 37.4849250909
40.5845117698 39.1462089236 35.1171578292 34.945165344
45.1067352961 40.523040106 40.6095830913 39.0957278345
41.3221140974 38.1947918393 39.9036867306 37.7696131032
41.8244654995 40.1567131661 38.0674700168 35.1089144603
45.4976929401 45.5597962603 42.7258732951 43.2422832585
[10 rows x 4 columns]
  • Computing the number of correct predictions using 1-nearest neighbors for the dog class: Now that you have created the SFrame dog_distances, you will learn to use the method
.apply()

on this SFrame to iterate line by line and compute the number of ‘dog’ test examples where the distance to the nearest ‘dog’ was lower than that to the other classes. You will do this in three steps:

i. Consider one row of the SFrame dog_distances. Let’s call this variable row. You can access each distance by calling, for example,

row[‘dog_cat’]

which, in example table above, will have value equal to 36.4196077068 for the first row.

Create a function starting with

def is_dog_correct(row):

which returns 1 if the value for row[‘dog_dog’] is lower than that of the other columns, and 0 otherwise. That is, returns 1 if this row is correctly classified by 1-nearest neighbors, and 0 otherwise.


In [71]:
def is_dog_correct(row):  
    if row['dog_dog'] <= min(row.values()):     
        return 1    
    else:        
        return 0

# dog_distances.apply(lambda row: 1 if row['dog_dog'] <= min(row.values()) else 0)

ii. Using the function is_dog_correct(row), you can check if 1 row is correctly classified. Now, you want to count how many rows are correctly classified. You could do a for loop iterating through each row and applying the function is_dog_correct(row). This method will be really slow, because the SFrame is not optimized for this type of operation.

Instead, we will use the .apply() method to iterate the function is_dog_correct for each row of the SFrame.

Read about using the .apply() method here.

iii. Computing the number of correct predictions for ‘dog’: You can now call:

dog_distances.apply(is_dog_correct)

which will return an SArray (a column of data) with a 1 for every correct row and a 0 for every incorrect one.


In [73]:
dog_distances.apply(is_dog_correct)


Out[73]:
dtype: int
Rows: 1000
[1L, 1L, 1L, 1L, 0L, 1L, 1L, 1L, 1L, 0L, 1L, 0L, 0L, 1L, 1L, 1L, 1L, 0L, 0L, 1L, 0L, 0L, 0L, 0L, 1L, 1L, 0L, 1L, 1L, 0L, 1L, 1L, 0L, 1L, 1L, 1L, 1L, 1L, 0L, 0L, 1L, 1L, 0L, 1L, 1L, 0L, 1L, 1L, 1L, 1L, 0L, 0L, 1L, 1L, 0L, 1L, 1L, 1L, 0L, 0L, 1L, 0L, 0L, 1L, 0L, 1L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 0L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 0L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 0L, 1L, 1L, 1L, 1L, 1L, 1L, 0L, 0L, ... ]

You can call:

.sum()

on the result to get the total number of correctly classified ‘dog’ images in the test set!


In [74]:
dog_distances.apply(is_dog_correct).sum()


Out[74]:
678L

Hint: To make sure your code is working correctly, if you were to do steps d) and e) in this question to count the number of correctly classified ‘cat’ images in the test data, instead of ‘dog’, the result would be 548.


In [77]:
cat_distances = graphlab.SFrame({'cat_automobile': automobile_model.query(image_test_cat, k=1)['distance'],
                                 'cat_bird': bird_model.query(image_test_cat, k=1)['distance'],
                                 'cat_cat': cat_model.query(image_test_cat, k=1)['distance'],
                                 'cat_dog': dog_model.query(image_test_cat, k=1)['distance'],
                                })


PROGRESS: Starting blockwise querying.
PROGRESS: max rows per data block: 7668
PROGRESS: number of reference data blocks: 4
PROGRESS: number of query data blocks: 1
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 1000         | 127000  | 24.9509     | 509ms        |
PROGRESS: | Done         | 509000  | 100         | 530ms        |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: Starting blockwise querying.
PROGRESS: max rows per data block: 7668
PROGRESS: number of reference data blocks: 4
PROGRESS: number of query data blocks: 1
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 1000         | 120000  | 25.1046     | 512ms        |
PROGRESS: | Done         | 478000  | 100         | 574ms        |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: Starting blockwise querying.
PROGRESS: max rows per data block: 7668
PROGRESS: number of reference data blocks: 4
PROGRESS: number of query data blocks: 1
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 1000         | 127000  | 24.9509     | 490ms        |
PROGRESS: | Done         | 509000  | 100         | 533ms        |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: Starting blockwise querying.
PROGRESS: max rows per data block: 7668
PROGRESS: number of reference data blocks: 4
PROGRESS: number of query data blocks: 1
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | Query points | # Pairs | % Complete. | Elapsed Time |
PROGRESS: +--------------+---------+-------------+--------------+
PROGRESS: | 1000         | 127000  | 24.9509     | 479ms        |
PROGRESS: | Done         | 509000  | 100         | 508ms        |
PROGRESS: +--------------+---------+-------------+--------------+

In [78]:
cat_distances.head()


Out[78]:
cat_automobile cat_bird cat_cat cat_dog
39.6710582792 38.074265869 34.623719208 37.4642628784
43.0089056688 36.3674024138 33.8680579302 29.3472319585
38.6010006604 35.3039394947 32.4615168902 32.2599640475
39.3566307091 38.8944029601 35.7708210254 35.3852085188
38.3572372618 34.2820409875 31.1577686417 30.0442985088
42.0904793181 44.5352170178 41.3986035847 35.4741000424
39.0520251253 34.0290595084 30.9894594959 32.5845275226
39.3058645069 39.0236924983 37.0814607387 37.6502852614
43.0248129799 40.8334054297 39.9883863688 36.9801353512
45.6749176426 40.1258835601 39.7076633097 41.1259410707
[10 rows x 4 columns]

In [81]:
def is_cat_correct(row):  
    if row['cat_cat'] <= min(row.values()):     
        return 1    
    else:        
        return 0

In [82]:
cat_distances.apply(is_cat_correct).sum()


Out[82]:
548L
  • Accuracy of predicting dog in the test data: Using the work you did in this question, what is the accuracy of the 1-nearest neighbor classifier at classifying ‘dog’ images from the test set?

In [87]:
dog_distances.apply(is_dog_correct).sum()/float(len(dog_distances))


Out[87]:
0.678